package com.gzist.elderlyserviceplatform.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.StrUtil;
import com.gzist.elderlyserviceplatform.config.MinioConfig;
import com.gzist.elderlyserviceplatform.entity.dto.ChunkUploadDTO;
import com.gzist.elderlyserviceplatform.entity.po.File;
import com.gzist.elderlyserviceplatform.interceptors.exceptions.FileUploadFailedException;
import com.gzist.elderlyserviceplatform.service.FileService;
import com.gzist.elderlyserviceplatform.service.MinioFileService;
import com.gzist.elderlyserviceplatform.utils.MinioUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * minio文件服务实现类
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class MinioFileServiceImpl implements MinioFileService {

    private static final String CHUNK_DIR = "chunks/";

    private final MinioUtil minioUtil;
    private final MinioConfig minioConfig;
    private final FileService fileService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String uploadFile(MultipartFile file) {
        try {
            String bucketName = minioConfig.getBucketName();
            String objectName = generateObjectName(file.getOriginalFilename());
            minioUtil.uploadFile(bucketName, file, objectName, file.getContentType());

            // 构建文件访问 URL
            String fileUrl = minioConfig.getEndpoint() + "/" + bucketName + "/" + objectName;

            // 保存文件信息到数据库
            File fileEntity = new File();
            fileEntity.setFileName(file.getOriginalFilename());
            fileEntity.setFileType(file.getContentType());
            fileEntity.setFileSize(String.valueOf(file.getSize()));
            fileEntity.setBucketName(bucketName);
            fileEntity.setObjectName(objectName);
            fileEntity.setFilePath(minioConfig.getEndpoint() + "/" + bucketName + "/");
            fileEntity.setUploaderId(StpUtil.getLoginId("admin"));
            fileEntity.setUrl(fileUrl);
            fileService.save(fileEntity);

            return fileEntity.getUrl();
        } catch (Exception e) {
            log.error("File upload failed", e);
            throw new FileUploadFailedException(e.getMessage());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String uploadFileByStream(InputStream inputStream, String fileType, String fileName) {
        try {
            String bucketName = minioConfig.getBucketName();
            String objectName = generateObjectName(fileName);
            minioUtil.uploadFile(bucketName, objectName, inputStream);

            // 构建文件访问 URL
            String fileUrl = minioConfig.getEndpoint() + "/" + bucketName + "/" + objectName;

            // 保存文件信息到数据库
            File fileEntity = new File();
            fileEntity.setFileName(fileName);
            fileEntity.setFileType(fileType);
            fileEntity.setBucketName(bucketName);
            fileEntity.setFilePath(minioConfig.getEndpoint() + "/" + bucketName + "/");
            fileEntity.setObjectName(objectName);
            fileEntity.setUploaderId(StpUtil.getLoginId("admin"));
            fileEntity.setUrl(fileUrl);
            fileService.save(fileEntity);

            return fileEntity.getUrl();
        } catch (Exception e) {
            log.error("File upload failed", e);
            throw new FileUploadFailedException(e.getMessage());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String uploadImage(String imageBase64) {
        try {
            String bucketName = minioConfig.getBucketName();
            String objectName = generateObjectName("image.jpg");
            minioUtil.uploadImage(bucketName, imageBase64, objectName);

            // 构建文件访问 URL
            String fileUrl = minioConfig.getEndpoint() + "/" + bucketName + "/" + objectName;

            // 保存文件信息到数据库
            File fileEntity = new File();
            fileEntity.setFileName("image.jpg");
            fileEntity.setFileType("image/jpeg");
            fileEntity.setFileSize(String.valueOf(imageBase64.length()));
            fileEntity.setBucketName(bucketName);
            fileEntity.setFilePath(minioConfig.getEndpoint() + "/" + bucketName + "/");
            fileEntity.setObjectName(objectName);
            fileEntity.setUploaderId(StpUtil.getLoginId("admin"));
            fileEntity.setUrl(fileUrl);
            fileService.save(fileEntity);

            return fileEntity.getUrl();
        } catch (Exception e) {
            log.error("Image upload failed", e);
            throw new FileUploadFailedException(e.getMessage());
        }
    }

    @Override
    public void deleteFile(String id) {
        try {
            File fileEntity = fileService.getById(id);
            if (fileEntity == null) {
                throw new FileUploadFailedException("File not found");
            }
            minioUtil.removeFile(fileEntity.getBucketName(), fileEntity.getObjectName());
            fileService.removeById(id);
        } catch (Exception e) {
            log.error("File deletion failed", e);
            throw new FileUploadFailedException(e.getMessage());
        }
    }

    @Override
    public void deleteFiles(List<String> ids) {
        try {
            for (String id : ids) {
                File fileEntity = fileService.getById(id);
                if (fileEntity == null) {
                    throw new FileUploadFailedException("File not found");
                }
                minioUtil.removeFile(fileEntity.getBucketName(), fileEntity.getObjectName());
                fileService.removeById(id);
            }
        } catch (Exception e) {
            log.error("Batch deletion failed", e);
            throw new FileUploadFailedException(e.getMessage());
        }
    }

    @Override
    public String getFileUrl(String id) {
        try {
            File fileEntity = fileService.getById(id);
            if (fileEntity == null) {
                throw new FileUploadFailedException("File not found");
            }
            return minioConfig.getEndpoint() + "/" + fileEntity.getBucketName() + "/" + fileEntity.getObjectName();
        } catch (Exception e) {
            log.error("Failed to retrieve file URL", e);
            throw new FileUploadFailedException(e.getMessage());
        }
    }

    @Override
    public Map<String, Object> checkChunkStatus(String fileMd5, Integer totalChunks) {
        Map<String, Object> result = new HashMap<>();
        String chunkPrefix = CHUNK_DIR + fileMd5 + "/";

        try {
            List<String> existingChunks = minioUtil.listChunkObjects(
                    minioConfig.getBucketName(),
                    chunkPrefix
            );

            List<Integer> uploaded = existingChunks.stream()
                    .map(name -> Integer.parseInt(name.split("_")[1]))
                    .sorted()
                    .collect(Collectors.toList());

            result.put("uploaded", uploaded);
            result.put("needUpload", uploaded.size() < totalChunks);
            return result;
        } catch (Exception e) {
            log.error("分片检查失败", e);
            throw new FileUploadFailedException("分片检查失败");
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String uploadChunkAndMerge(MultipartFile chunk,ChunkUploadDTO dto) { // 新增总分片数参数
        String finalObject = null;
        try {
            // 1. 上传分片
            String chunkName = CHUNK_DIR + dto.getFileMd5() + "/part_" + dto.getChunkIndex();
            minioUtil.uploadChunk(
                    minioConfig.getBucketName(),
                    chunkName,
                    chunk.getInputStream(),
                    chunk.getSize()
            );

            // 2. 检查是否全部上传完成
            if (isAllChunksUploaded(dto.getFileMd5(), dto.getTotalChunks())) {
                // 3. 执行合并操作
                String chunkPrefix = CHUNK_DIR + dto.getFileMd5() + "/";
                List<String> chunkObjects = minioUtil.listChunkObjects(
                        minioConfig.getBucketName(),
                        chunkPrefix
                );

                finalObject = generateObjectName(dto.getFileName());
                minioUtil.composeChunks(
                        minioConfig.getBucketName(),
                        chunkObjects,
                        finalObject
                );

                // 4. 清理分片
                minioUtil.removeObjects(
                        minioConfig.getBucketName(),
                        chunkObjects
                );
                // 构建文件访问 URL
                String fileUrl = minioConfig.getEndpoint() + "/" + minioConfig.getBucketName() + "/" + finalObject;

                // 5. 保存文件记录（与原逻辑一致）
                File fileEntity = new File();
                fileEntity.setFileName(dto.getFileName());
                fileEntity.setFileType(dto.getFileType());
                fileEntity.setFileMd5(dto.getFileMd5());
                fileEntity.setBucketName(minioConfig.getBucketName());
                fileEntity.setObjectName(finalObject);
                fileEntity.setFilePath(minioConfig.getEndpoint() + "/" + finalObject);
                fileEntity.setUrl(fileUrl);
                fileEntity.setUploaderId(StpUtil.getLoginId("admin"));
                fileEntity.setCreateTime(LocalDateTime.now());

                Long fileSize = minioUtil.getObjectSize(
                        minioConfig.getBucketName(),
                        finalObject
                );
                fileEntity.setFileSize(String.valueOf(fileSize));

                fileService.save(fileEntity);

                return fileEntity.getUrl();
            }

            return "分片上传成功，等待其他分片"; // 非最终合并时返回提示
        } catch (Exception e) {
            log.error("分片处理失败 fileMd5={}", dto.getFileMd5(), e);
            if (finalObject != null) {
                minioUtil.removeFile(
                        minioConfig.getBucketName(),
                        finalObject
                );
            }
            throw new FileUploadFailedException("文件处理失败: " + e.getMessage());
        }
    }

    /**
     * 检查所有分片是否上传完成
     */
    private boolean isAllChunksUploaded(String fileMd5, int totalChunks) {
        // 方式1：通过MinIO API查询分片数量
        String chunkPrefix = CHUNK_DIR + fileMd5 + "/";
        return minioUtil.listChunkObjects(
                minioConfig.getBucketName(),
                chunkPrefix
        ).size() >= totalChunks; // >= 防止重复上传

        // 方式2：使用Redis计数器（推荐用于高并发场景）
        // Long count = redisTemplate.opsForValue().increment("upload:count:" + fileMd5);
        // return count != null && count >= totalChunks;
    }

    /**
     * 生成对象名称
     *
     * @param fileName 原始文件名，用于生成对象名称的一部分
     * @return 返回生成的唯一对象名称
     */
    private String generateObjectName(String fileName) {
        String random = StrUtil.uuid().replace("-", "");
        return random + "_" + fileName;
    }
}
